package ru.spb.nicetu.altx.mnemo.node;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;

import ru.spb.nicetu.altx.mnemo.cell.INodeCell;
import ru.spb.nicetu.altx.mnemo.cell.ManagedNodeCell;
import ru.spb.nicetu.altx.mnemo.cell.INodeCell.Categories;
import ru.spb.nicetu.altx.mnemo.extpoint.valuetypes.ITemplateValueType;

//@SuppressWarnings("serial")
public class CompoundTaskNode extends BaseNode implements ICompoundTaskNode {

	
  public CompoundTaskNode(String name) {
    super(name);
  }

  /**
   * Каждой Виртуальной Ячейке NODE-а (ВХодной proxy-ячейке) сопоставляется
   * ячейка какого-либо другого NODE-а (оригинальная ВХодная ячейка (задаваемая
   * ссылкой ICellLink)).
   */
  private Map<INodeCell, ICellLink> linksToOriginalCells = new HashMap<INodeCell, ICellLink>();

  /**
   * Каждой Виртуальной Ячейке NODE-а (ВЫХодной proxy-ячейке) сопоставляется
   * коллекция потребителей этой proxy-ячейки (ВЫХодных ячеек других NODE-ов
   * (задаваемых ссылками ICellLink))
   */
  private Map<INodeCell, Collection<ICellLink>> linksFromMyOutputCells = new HashMap<INodeCell, Collection<ICellLink>>();

  
  public INodeCell addVirtualInputCell(ICellLink link) {
    INodeCell myCell = createVirtualCell(link);

    addNodeCell(myCell);
    linksToOriginalCells.put(myCell, link);
    return myCell;
  }

  
  public INodeCell addVirtualOutputCell(ICellLink link) {
    INodeCell myCell = createVirtualCell(link);

    addNodeCell(myCell);
    linksToOriginalCells.put(myCell, link);
    return myCell;
  }

  
  public void removeVirtualInputCell(ICellLink link) {
    INodeCell myCell = createVirtualCell(link);

    removeInputNodeCell(myCell);
    linksToOriginalCells.remove(myCell);
  }

  
  public void removeVirtualOutputCell(ICellLink link) {
    INodeCell myCell = createVirtualCell(link);

    removeOutputNodeCell(myCell);
    linksToOriginalCells.remove(myCell);
  }

  
  public ICellLink getLinkToOriginalInputCell(INodeCell cell) {
    return linksToOriginalCells.get(cell);
  }

  
  public ICellLink getLinkToOriginalOutputCell(INodeCell cell) {
    return linksToOriginalCells.get(cell);
  }

  
  public ICellLink getVirtualCellLink(ICellLink originalCellLink) {
    // Виртуальная ячейка для Оригинальной ячейки в другом NODE-е.
    INodeCell tempCell = createVirtualCell(originalCellLink);

    // ...дополнительная проверка...
    ICellLink myLink = linksToOriginalCells.get(tempCell);
    if (myLink == null)
      return null;

    ICellLink link = new CellLink(this, tempCell);
    return link;
  }

  // *****************************************************************************
  // *****************************************************************************
  // *****************************************************************************

  
  public void addNodeCell(INodeCell cell) {
    super.addNodeCell(cell);
    if (cell.getCategorie() == Categories.OUTPUT)
      linksFromMyOutputCells.put(cell, new TreeSet<ICellLink>());
  }

  // 
  // public void addOutputNodeCell(INodeCell cell) {
  // super.addOutputNodeCell(cell);
  // //XXX CompoundTaskNode::addOutputNodeCell используется TreeSet, хочется
  // HashSet
  // linksFromMyOutputCells.put(cell,new TreeSet<ICellLink>());
  // }

  /**
   * создание ячейки с тем же типом хранимого значения и той же категории, что и
   * ячейка из link другого NODE-а. Имя создаваемой ячейки строится из имен
   * другого NODE-а и его ячейки.
   * 
   * @param link
   *          - ссылка на ячейку другого NODE-а.
   * @return созданная ячейка, соответствующая ячейке другого NODE-а.
   */
  protected INodeCell createVirtualCell(ICellLink link) {
    INode node = link.getNode();
    INodeCell cell = link.getCell();

    String nodeID = Long.toString(node.getID());
    String cellName = cell.getName();
    String newName = nodeID + "_" + cellName;

    INodeCell myCell = new ManagedNodeCell(cell.getCellID(), newName, cell
        .getValueClassName(), cell.getCategorie());

    return myCell;
  }

  
  public void linkMeToNode(INodeCell myCell, ICellLink link) {
    // ссылка на Оригинальную ячейку, сопоставлененой нашей ячейке.
    ICellLink linkToOriginalCell = getLinkToOriginalInputCell(myCell);
    // ICellLink linkToOriginalCell = linksToOriginalCells.get(myCell);

    // оригинальная ячейка и владелец оригинальной ячейки
    INodeCell originalCell = linkToOriginalCell.getCell();
    INode originalNode = linkToOriginalCell.getNode();

    // заносим ссылку на "источник значений" в оригинальную ячейку
    originalNode.linkMeToNode(originalCell, link);

    // super.linkMeToNode(myCell, link);
  }

  
  public void removeLinksToNode(INode node) {
    // FIXME CompoundTaskNode.removeLinksToNode - Override (serg)
    // проходим по все Виртуальным входным ячейкам
    for (INodeCell proxyCell : this.getAllInputCells()) {
      // ссылка на Оригинальную ячейку, сопоставлененой нашей ячейке.
      ICellLink linkToOriginalCell = getLinkToOriginalInputCell(proxyCell);
      // оригинальная ячейка и владелец оригинальной ячейки
      INodeCell originalCell = linkToOriginalCell.getCell();
      INode originalNode = linkToOriginalCell.getNode();

      // Узнаем, является ли ячейка node-а источником данных для нашей ячейки
      ICellLink link = originalNode.getLink(originalCell);
      if (link == null)
        continue;
      INode srcNode = link.getNode();
      if (srcNode.equals(node)) {
        originalNode.linkMeToNode(originalCell, null);
      }
    }
  }

  
  public void removeOutputNodeCell(INodeCell cell) {
    // Удалить ссылку на свою ячейку со своих потребителей
    Collection<ICellLink> links = linksFromMyOutputCells.get(cell);
    if (links == null){
      super.removeOutputNodeCell(cell);
      return;
    }
    for (ICellLink link : links.toArray(new ICellLink[0])) {
      INode dstNode = link.getNode();
      // INodeCell dstCell = link.getCell();
      if (!(dstNode instanceof DataNode)) {
        System.out.println("ERROR!!! ERROR!!! ERROR!!! "
            + "Подсоединенный узел должен быть instanceof DataNode");
      }
      dstNode.removeLinksToNode(this);
    }
    linksFromMyOutputCells.remove(cell);

    super.removeOutputNodeCell(cell);
  }

  
  public void doLinkToDestinationCell(INode node, INodeCell cell, ICellLink link) {

    INodeCell myVirtualCell = link.getCell();
    ICellLink linkToDest = new CellLink(node, cell);

    Collection<ICellLink> links = linksFromMyOutputCells.get(myVirtualCell);
    
    if (links == null){ links = new ArrayList<ICellLink>(0);
    linksFromMyOutputCells.put(myVirtualCell, links); }
    
    links.add(linkToDest);
    
    // linkSet.add(linkToDest);
    traceLinkToDst();
  }

  
  public void doUnlinkToDestinationCell(INode node, INodeCell cell,
                                        ICellLink link) {
    INodeCell myVirtualCell = link.getCell();
    ICellLink linkToDest = new CellLink(node, cell);

    Collection<ICellLink> links = linksFromMyOutputCells.get(myVirtualCell);
    links.remove(linkToDest);
    traceLinkToDst();
  }

  private void traceLinkToDst() {
    System.out.println("==== Links To Destination:");
    for (INodeCell outCell : linksFromMyOutputCells.keySet()) {
      System.out
          .println(this.getConcreteName() + "." + outCell.getName() + ":");
      for (ICellLink link : linksFromMyOutputCells.get(outCell)) {
        INode dstNode = link.getNode();
        INodeCell dstCell = link.getCell();
        // FIXME
//        System.out.println("\t" + dstNode.getConcreteName() + "." +
//            dstCell.getName());
      }
    }
  }

  
  public void calcGoodValueSourceCondition() {
    setGoodValueSourceCondition(true);

    for (INodeCell proxyCell : this.getAllInputCells()) {
      ICellLink linkToOriginalCell = getLinkToOriginalInputCell(proxyCell);
      INodeCell originalCell = linkToOriginalCell.getCell();
      INode originalNode = linkToOriginalCell.getNode();

      // FIXME temp bugfig
      if (originalNode == null) {
        setGoodValueSourceCondition(false);
        
        try {
          throw new Exception("Проблема с ячейками в Node-е "+getName()+" ["+getID()+"]");
        } catch (Exception e) {
          e.printStackTrace();
        }        
        return;
      }
     // end FIXME temp bugfig
      
      
      ICellLink link = originalNode.getLink(originalCell);
      if (link == null) {
        setGoodValueSourceCondition(false);
        return;
      }
    }
  }

  
  public void run() {
    fWasRunned = true;
    fInterrupted = true;
    // super.run();
  }

  @SuppressWarnings("unchecked")
  
  public ITemplateValueType getCellValue(INodeCell cell) {
    ICellLink linkToOriginalCell = getLinkToOriginalInputCell(cell);
    if (linkToOriginalCell == null)
      return null;

    INode originalNode = linkToOriginalCell.getNode();
    if (!(originalNode instanceof DataNode))
      return null;

    return originalNode.getCellValue(originalNode.getAllInputCells().get(0));
  }

  
  public boolean isReadyToRun() {
    // !!! Должны быть выполнены все isInput-DataNode-ы внутри Compound-а
    for (INodeCell cell : getAllInputCells()) {
      ICellLink linkToOriginalCell = getLinkToOriginalInputCell(cell);
      if (linkToOriginalCell == null)
        return false;

      INode node = linkToOriginalCell.getNode();
      if (node == null)
        return false;

      if (!node.wasRunned())
        return false;
    }
    return true;
  }

  
  public boolean wasRunned() {
    return fWasRunned;
    /*
     * //FIXME (Arkady) Неизвестна корректность метода 
     * for (INodeCell cell: getAllOutputCells()){
     *  ICellLink link = linksToOriginalCells.get(cell);
     * if(link == null) continue; if (! (link.getNode() instanceof IDataNode) )
     * continue; if (! (link.getNode().wasRunned()) ) return false; } return
     * true;
     */
  }

  
  public boolean hasDataForNodeRun(ICellLink link) {
    if (link == null)
      return false;

    INode targetNode = link.getNode();
    if (!(targetNode instanceof IDataNode))
      return false;
    IDataNode dataNode = (IDataNode) targetNode;

    INodeCell targetCell = link.getCell();
    if (targetCell == null)
      return false;

    ICellLink link2Me = dataNode.getLink(targetCell);
    if (link2Me == null)
      return false;

    if (link2Me.getNode() != this)
      return false;

    INodeCell myCell = link2Me.getCell();
    ICellLink link2original = linksToOriginalCells.get(myCell);
    if (link2original == null)
      return false;

    INode srcNode = link2original.getNode();
    if (srcNode == null)
      return false;

    return srcNode.wasRunned();
  }

}
